import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;

/**
 * https://blog.csdn.net/f641385712/article/details/89417895
 */
public class T07_AopUtil {

    public static void main(String[] args) {
        HelloService helloService = getProxy(new HelloServiceImpl());
        // ===============演示AopUtils==================

        // AopUtils.isAopProxy：是否是代理对象
        System.out.println(AopUtils.isAopProxy(helloService)); // true
        System.out.println(AopUtils.isJdkDynamicProxy(helloService)); // false
        System.out.println(AopUtils.isCglibProxy(helloService)); // true

        // 拿到目标对象
        System.out.println(AopUtils.getTargetClass(helloService)); // class aop.AopUtilTest$HelloServiceImpl

        // selectInvocableMethod：方法 @since 4.3  底层依赖于方法 MethodIntrospector.selectInvocableMethod
        // 只是在它技术上做了一个判断： 必须是被代理的方法才行（targetType 是 SpringProxy 的子类，且是 private 这种方法，且不是 static 的就不行）
        // Spring MVC 的 detectHandlerMethods 对此方法有大量调用
        Method method = ClassUtils.getMethod(HelloServiceImpl.class, "hello");
        // public java.lang.Object aop.AopUtilTest$HelloServiceImpl.hello()
        System.out.println(AopUtils.selectInvocableMethod(method, HelloServiceImpl.class));

        // 是否是equals方法
        // isToStringMethod、isHashCodeMethod、isFinalizeMethod  都是类似的
        System.out.println(AopUtils.isEqualsMethod(method)); // false

        // 它是对 ClassUtils.getMostSpecificMethod，增加了对代理对象的特殊处理
        System.out.println(AopUtils.getMostSpecificMethod(method, HelloService.class));

        // ===============演示AopProxyUtils==================

        // 注意：此处的入参必须是一个 Advised：也就是被代理过的对象，否则返回null
        // 里面的 TargetSource 必须是 SingletonTargetSource 才会有所返回
        // @since 4.3.8
        System.out.println(AopProxyUtils.getSingletonTarget(helloService)); // aop.AopUtilTest$HelloServiceImpl@5ea434c8
        // 获取一个代理对象的最终对象类型
        System.out.println(AopProxyUtils.ultimateTargetClass(helloService)); // class aop.AopUtilTest$HelloServiceImpl
    }

    private static HelloService getProxy(Object targetObj) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(targetObj);
        proxyFactory.addAdvice((MethodBeforeAdvice) (method, args1, target) -> {
            System.out.println("方法之前执行了~~~");
        });

        HelloService helloService = (HelloService) proxyFactory.getProxy();
        helloService.hello();
        System.out.println(helloService.getClass().getName()); // HelloServiceImpl$$EnhancerBySpringCGLIB$$9b28670f
        return helloService;
    }

    interface HelloService {
        Object hello();
    }

    static class HelloServiceImpl implements HelloService {
        @Override
        public Object hello() {
            System.out.println("this is my method~~");
            return "service hello";
        }

        // 准备一个私有方法，测试用
        private void privateMethod() {
            System.out.println("privateMethod");
        }
    }
}
